// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

// A client sending requests to server every 1 second.

#include <gflags/gflags.h>
#include <butil/logging.h>
#include <butil/time.h>
#include <brpc/channel.h>
#include "echo.pb.h"
#include <opencv2/opencv.hpp>

DEFINE_string(attachment, "", "Carry this along with requests");
DEFINE_string(protocol, "baidu_std", "Protocol type. Defined in src/brpc/options.proto");
DEFINE_string(connection_type, "", "Connection type. Available values: single, pooled, short");  // 单连接、连接池、短连接
DEFINE_string(server, "0.0.0.0:8000", "IP Address of server");
DEFINE_string(load_balancer, "", "The algorithm for load balancing");
DEFINE_int32(timeout_ms, 100, "RPC timeout in milliseconds");
DEFINE_int32(max_retry, 3, "Max retries(not including the first RPC)"); 
DEFINE_int32(interval_ms, 1000, "Milliseconds between consecutive requests");
DEFINE_string(image_path, "", "Path to the image file to send");

// usage: ./echo_client --image_path=../car.png

int main(int argc, char* argv[]) {
    // 推荐使用gflags解析参数
    GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);

    // 创建ChannelOptions，用于配置Channel
    brpc::ChannelOptions options;
    options.protocol = FLAGS_protocol;                // 协议类型
    options.connection_type = FLAGS_connection_type;  // 连接类型
    options.timeout_ms = FLAGS_timeout_ms;            // 超时时间
    options.max_retry = FLAGS_max_retry;              // 最大重试次数

    // Channel表示连接服务器的通信线路
    // Channel是线程安全的，可被程序中的所有线程共享
    brpc::Channel channel;

    // 初始化Channel，传入服务器地址、负载均衡策略、options(为空则使用默认选项)
    if (channel.Init(FLAGS_server.c_str(), FLAGS_load_balancer.c_str(), &options) != 0) {
        LOG(ERROR) << "Fail to initialize channel";
        return -1;
    }

    // 不能直接调用Channel，而应构建一个stub服务来封装它，stub也可被所有线程共享
    example::EchoService_Stub stub(&channel);

    // 读取图像
    cv::Mat image = cv::imread(FLAGS_image_path);
    if (image.empty()) {
        LOG(ERROR) << "Failed to load image from: " << FLAGS_image_path;
        return -1;
    }

    // 每秒发送一个请求并等待响应
    int log_id = 0;
    while (!brpc::IsAskedToQuit()) {
        example::EchoRequest request;
        example::EchoResponse response;
        brpc::Controller cntl;

        // 设置图像数据
        example::ImageData* img_data = request.mutable_image();
        img_data->set_width(image.cols);
        img_data->set_height(image.rows);
        img_data->set_channels(image.channels());

        // 将图像数据序列化，进行二进制数据的传输
        std::string img_binary(reinterpret_cast<char*>(image.data), image.total() * image.elemSize());
        img_data->set_data(img_binary);

        // 设置图像文件名
        request.set_image_name("received_image.png");

        cntl.set_log_id(log_id++);
        // 在请求中设置直接连接到网络的附加数据，不会序列化为protobuf消息
        cntl.request_attachment().append(FLAGS_attachment);

        // 发送请求
        // 由于 "done"（最后一个参数）为 NULL，因此该函数将等待响应返回或错误发生(包括超时)
        stub.Echo(&cntl, &request, &response, NULL);
        // 检查结果
        if (!cntl.Failed()) {
            LOG(INFO) << "Image sent successfully. Received response from "
                << cntl.remote_side() << " to " << cntl.local_side()
                << ": " << response.message() << " (attached="
                << cntl.response_attachment() << ")"
                << " latency=" << cntl.latency_us() << "us";
        } else {
            LOG(WARNING) << cntl.ErrorText();
        }
        usleep(FLAGS_interval_ms * 1000L);
    }

    LOG(INFO) << "EchoClient is going to quit";
    return 0;
}
